﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Brjnk.Common;

namespace Brjnk.GpxExplorer.UI.Controls
{
    /// <summary>
    /// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file.
    ///
    /// Step 1a) Using this custom control in a XAML file that exists in the current project.
    /// Add this XmlNamespace attribute to the root element of the markup file where it is
    /// to be used:
    ///
    ///     xmlns:MyNamespace="clr-namespace:Brjnk.GpxExplorer.UI.Controls"
    ///
    ///
    /// Step 1b) Using this custom control in a XAML file that exists in a different project.
    /// Add this XmlNamespace attribute to the root element of the markup file where it is
    /// to be used:
    ///
    ///     xmlns:MyNamespace="clr-namespace:Brjnk.GpxExplorer.UI.Controls;assembly=Brjnk.GpxExplorer.UI.Controls"
    ///
    /// You will also need to add a project reference from the project where the XAML file lives
    /// to this project and Rebuild to avoid compilation errors:
    ///
    ///     Right click on the target project in the Solution Explorer and
    ///     "Add Reference"->"Projects"->[Browse to and select this project]
    ///
    ///
    /// Step 2)
    /// Go ahead and use your control in the XAML file.
    ///
    ///     <MyNamespace:MapRotationPicker/>
    ///
    /// </summary>
    public class MapRotationPicker : Control
    {
        static MapRotationPicker()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(MapRotationPicker), new FrameworkPropertyMetadata(typeof(MapRotationPicker)));
        }

        public MapRotationPicker()
        {
            this.MouseMove += new MouseEventHandler(MapRotationPicker_MouseMove);
            this.MouseDown += new MouseButtonEventHandler(MapRotationPicker_MouseDown);
            this.MouseWheel += new MouseWheelEventHandler(MapRotationPicker_MouseWheel);
        }

        private void MapRotationPicker_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            this.SetCurrentValue(AngleProperty, Angle + (e.Delta / 120.0) * 4);
        }

        private void MapRotationPicker_MouseDown(object sender, MouseButtonEventArgs e)
        {
            if (e.ClickCount == 2) this.SetCurrentValue(AngleProperty, 0.0);
        }

        private void MapRotationPicker_MouseMove(object sender, MouseEventArgs e)
        {
            double currentAngle, radius;
            GetPolar(e.GetPosition(this), out currentAngle, out radius);

            if (e.LeftButton == MouseButtonState.Pressed && radius > 5)
            {
                this.SetCurrentValue(AngleProperty, Angle + currentAngle - cursorAngle);
            }
            cursorAngle = currentAngle;
        }

        private double cursorAngle;

        private void GetPolar(Point point, out double angle, out double radius)
        {
            Point center = new Point(this.ActualWidth / 2, this.ActualHeight / 2);
            var rad = Math.Atan2(point.X - center.X, center.Y - point.Y);
            if (rad < 0) rad += 2 * Math.PI;
            angle = MathEx.ToDegrees(rad);
            radius = (center - point).Length;
        }

        protected override Size MeasureOverride(Size constraint)
        {
            if (double.IsPositiveInfinity(constraint.Width) && double.IsPositiveInfinity(constraint.Height))
            {
                return new Size(96, 96);
            }
            else if (double.IsPositiveInfinity(constraint.Width))
            {
                return new Size(constraint.Height, constraint.Height);
            }
            else if (double.IsPositiveInfinity(constraint.Height))
            {
                return new Size(constraint.Width, constraint.Width);
            }
            else
            {
                var size = Math.Min(constraint.Width, constraint.Height);
                return new Size(size, size);
            }
        }

        public double Angle
        {
            get { return (double)GetValue(AngleProperty); }
            set { SetValue(AngleProperty, value); }
        }

        public static readonly DependencyProperty AngleProperty =
            DependencyProperty.Register("Angle", typeof(double), typeof(MapRotationPicker), new UIPropertyMetadata(0.0) { CoerceValueCallback = CoerceAngle });

        public static object CoerceAngle(DependencyObject o, object value)
        {
            double val = (double)value;
            if (val < 0)
            {
                val = val - ((int)val / 360) * 360;
                val += 360;
            }
            if (val >= 360)
            {
                val = val - ((int)val / 360) * 360;
            }
            return val;
        }
    }
}